package data

import (
	"encoding/json"
	"io/ioutil"
	"path"
	"reflect"
	"strings"
	"sync"

	"github.com/mitchellh/mapstructure"
)

type DataConfig struct {
	m *sync.Map
}

type Table struct {
	IndexList []interface{}
	m         *sync.Map
}

type DataTpl struct {
	Key interface{}
	Val interface{}
}

var data *DataConfig

func init() {
	data = new(DataConfig)
}

func newTable(datas []*DataTpl) *Table {
	t := &Table{IndexList: make([]interface{}, 0), m: new(sync.Map)}
	for _, data := range datas {
		t.addRow(data.Key, data.Val)
	}
	return t
}

func (t *Table) addRow(index interface{}, val interface{}) {
	t.IndexList = append(t.IndexList, index)
	t.m.Store(index, val)
}

func (t *Table) get(key interface{}) (r interface{}, ok bool) {
	r, ok = t.m.Load(key)
	return
}

func (t *Table) foreach(f func(val interface{}) bool) (interface{}, bool) {
	for _, key := range t.IndexList {
		sval, ok := t.get(key)
		if f(sval) {
			return sval, ok
		}
	}
	return nil, false
}

func (t *Table) foreachs(f func(val interface{}) bool) (r []interface{}) {
	for _, key := range t.IndexList {
		sval, _ := t.get(key)
		if !f(sval) {
			return
		}
		r = append(r, sval)
	}
	return
}

func (t *Table) filters(f func(val interface{}) bool) (r []interface{}) {
	for _, key := range t.IndexList {
		sval, _ := t.get(key)
		if f(sval) {
			r = append(r, sval)
		}
	}
	return
}

func (t *Table) allData() (rs []interface{}) {
	rs = make([]interface{}, 0)
	t.m.Range(func(key, val interface{}) bool {
		rs = append(rs, val)
		return true
	})
	return
}

func (d *DataConfig) init(pathstr string) (err error) {
	initMstruct()
	d.m = new(sync.Map)

	files, err := ioutil.ReadDir(pathstr)
	if err != nil {
		return
	}
	for _, f := range files {
		if f.IsDir() {
			continue
		}
		b, err := ioutil.ReadFile(pathstr + "/" + f.Name())
		if err != nil {
			return err
		}
		tableName := strings.TrimSuffix(f.Name(), path.Ext(f.Name()))
		if _, ok := mstruct[tableName]; !ok {
			continue
		}
		m := make(map[string][]interface{}, 0)
		err = json.Unmarshal(b, &m)
		if err != nil {
			return err
		}

		var datas []*DataTpl

		if v, ok := m[tableName]; ok {
			rft, _ := mstruct[tableName]
			for _, item := range v {
				val := reflect.New(rft).Interface()
				if err := mapToStruct(item, val); err != nil {
					return err
				}
				rval := reflect.ValueOf(val)
				for rval.Kind() == reflect.Ptr {
					rval = rval.Elem()
				}
				first := rval.Field(0).Interface()
				data := &DataTpl{Key: first, Val: rval.Interface()}
				datas = append(datas, data)
			}
			table := newTable(datas)
			d.putTable(tableName, table)
		}
	}
	return
}

func mapToStruct(m interface{}, dst interface{}) error {
	if err := mapstructure.Decode(m, dst); err != nil {
		return err
	}
	return nil
}

func (d *DataConfig) getTable(table string) (r *Table, ok bool) {
	val, ok := d.m.Load(table)
	if !ok {
		return
	}
	r = val.(*Table)
	return
}

func (d *DataConfig) get(table string, key interface{}) (r interface{}, ok bool) {
	t, ok := d.getTable(table)
	if !ok {
		return
	}
	return t.get(key)
}

func (d *DataConfig) putTable(table string, val *Table) {
	d.m.Store(table, val)
}

func Get(table string, key interface{}) (r interface{}, ok bool) {
	return data.get(table, key)
}

func Foreach(table string, f func(val interface{}) bool) (r interface{}, ok bool) {
	t, ok := data.getTable(table)
	if !ok {
		return
	}
	return t.foreach(f)
}

func Foreachs(table string, f func(val interface{}) bool) (r []interface{}, ok bool) {
	t, ok := data.getTable(table)
	if !ok {
		return
	}
	r = t.foreachs(f)
	return r, true
}

func Filters(table string, f func(val interface{}) bool) (r []interface{}, ok bool) {
	t, ok := data.getTable(table)
	if !ok {
		return
	}
	r = t.filters(f)
	return r, true
}

func GetAllDatas(table string) (r []interface{}, ok bool) {
	t, ok := data.getTable(table)
	if !ok {
		return
	}
	r = t.allData()
	return
}

func Init(pathStr string) (err error) {
	return data.init(pathStr)
}

func initMstruct() {

	mstruct["AltarSixDices"] = reflect.TypeOf(Dice{})
	mstruct["AltarTwentyDices"] = reflect.TypeOf(Dice{})

	mstruct["ShopDiscounts"] = reflect.TypeOf(ShopItem{})
	mstruct["ShopSpins"] = reflect.TypeOf(ShopItem{})
	mstruct["ShopGolds"] = reflect.TypeOf(ShopItem{})
	mstruct["ShopPets"] = reflect.TypeOf(ShopItem{})
	mstruct["ShopChests"] = reflect.TypeOf(ShopItem{})
	mstruct["ShopDiamonds"] = reflect.TypeOf(RechargeItem{})
	mstruct["ShopGuilds"] = reflect.TypeOf(ShopItem{})

	mstruct["PetExp1"] = reflect.TypeOf(PetExp{})
	mstruct["PetExp2"] = reflect.TypeOf(PetExp{})
	mstruct["PetExp3"] = reflect.TypeOf(PetExp{})

	mstruct["PetSkill1"] = reflect.TypeOf(PetSkill{})
	mstruct["PetSkill2"] = reflect.TypeOf(PetSkill{})
	mstruct["PetSkill3"] = reflect.TypeOf(PetSkill{})
	mstruct["PetSkill4"] = reflect.TypeOf(PetSkill{})

	mstruct["AdTurnTableReward1"] = reflect.TypeOf(AdTurnTableReward{})
	mstruct["AdTurnTableReward2"] = reflect.TypeOf(AdTurnTableReward{})

	mstruct["SecondLayers"] = reflect.TypeOf(FirstLayer{})
	mstruct["NewGuideFireMaps"] = reflect.TypeOf(FirstLayer{})
	mstruct["ActActivities"] = reflect.TypeOf(ActActivity{})
	mstruct["SlotActivities"] = reflect.TypeOf(SlotActivity{})
	mstruct["OthActivities"] = reflect.TypeOf(OtherActivity{})
	mstruct["LoginActActivities"] = reflect.TypeOf(OtherActivity{})
	mstruct["ActivityRankReward1"] = reflect.TypeOf(ActivityRankReward{})
	mstruct["ActivityRankReward2"] = reflect.TypeOf(ActivityRankReward{})
	mstruct["SlotActivityReward1"] = reflect.TypeOf(SlotActivityReward{})

	mstruct["WorldBossActivities"] = reflect.TypeOf(WorldBossActivity{})
	mstruct["BossDmgAccuBonusLst1"] = reflect.TypeOf(BossDmgAccuBonus{})
	mstruct["PlayerDmgAccuLevelLst"] = reflect.TypeOf(PlayerDmgAccuLevel{})

	mstruct["LevelCollectProp1"] = reflect.TypeOf(LevelCollectProp{})

	mstruct["CodeTextLangs"] = reflect.TypeOf(Language{})
}
